﻿//////////////////////////////////////////////////////////////////////////////
//
//  Copyright © 1998-2024 Glodon Company Limited.  All rights reserved.
//
//  Use of this software is subject to the terms of the Glodon license
//  agreement provided at the time of installation or download, or which
//  otherwise accompanies this software in either electronic or hard copy form.  
//
//////////////////////////////////////////////////////////////////////////////

// NotReviewedButNeeded
#pragma once
#include "NdbObjectContainer.h"
#include "NdbObjectArchiveHelper.h"
#include "NdbPropertySerialize.h"
#include "NdbReplaceElementIdHelper.h"
#include "NdbReportElementIdHelper.h"
#include "NdbCopyHelper.h"
#include "NdbObjectMemoryModelGroupNode.h"
#include "NdbArchiveBlockHeader.h"
#include "OrderSerializeUnorderedMapUtils.h"
#include "IIODevice.h"
#include <unordered_map>

namespace gcmp
{
    class IDbObjectArchive;
    template
    <
        class Key,
        class T,
        class Hash = std::hash<Key>,
        class KeyEqual = std::equal_to<Key>
    >
    class NdbUnorderedMap : public NdbObjectContainer
    {
        typedef std::true_type is_unordermap;
        // type/iterator
    public:
        typedef typename std::unordered_map<Key, T, Hash, KeyEqual>::key_type
            key_type;
        typedef typename std::unordered_map<Key, T, Hash, KeyEqual>::mapped_type
            mapped_type;
        typedef typename std::unordered_map<Key, T, Hash, KeyEqual>::size_type
            size_type;

        typedef typename std::unordered_map<Key, T, Hash, KeyEqual>::iterator
            iterator;
        typedef typename std::unordered_map<Key, T, Hash, KeyEqual>::const_iterator
            const_iterator;

        // constructor/destructor/operator=
    public:
        NdbUnorderedMap() {}

        NdbUnorderedMap& operator = (const NdbUnorderedMap& another)
        {
            if (this != &another)
            {
                m_umap = another.m_umap;
            }
            return *this;
        }

        NdbUnorderedMap& operator = (NdbUnorderedMap&& another)
        {
            if (this != &another)
            {
                m_umap = std::move(another.m_umap);
            }
            return *this;
        }

        NdbUnorderedMap& operator = (const std::unordered_map<Key, T, Hash, KeyEqual>& umap)
        {
            if (&m_umap != &umap)
            {
                m_umap = umap;
            }
            return *this;
        }

        NdbUnorderedMap& operator = (std::unordered_map<Key, T, Hash, KeyEqual>&& umap)
        {
            if (&m_umap != &umap)
            {
                m_umap = std::move(umap);
            }
            return *this;
        }

        const std::unordered_map<Key, T, Hash, KeyEqual>& StdUnorderedMap() const
        {
            return m_umap;
        }

        std::unordered_map<Key, T, Hash, KeyEqual>& StdUnorderedMapFW()
        {
            return m_umap;
        }

        operator const std::unordered_map<Key, T, Hash, KeyEqual>& () const
        {
            return m_umap;
        }

        operator std::unordered_map<Key, T, Hash, KeyEqual>& ()
        {
            return m_umap;
        }

        // Element access
    public:
        mapped_type& operator[](const key_type& k)
        {
            return m_umap[k];
        }

        mapped_type& operator[](key_type&& k)
        {
            return m_umap[k];
        }

        mapped_type& at(const key_type& k)
        {
            return m_umap.at(k);
        }

        const mapped_type& at(const key_type& k) const
        {
            return m_umap.at(k);
        }

        // Modifiers
    public:
        template <class P>
        std::pair<iterator, bool> insert(P&& val)
        {
            return m_umap.insert(std::forward<P>(val));
        }

        // Capacity
    public:
        bool empty() const
        {
            return m_umap.empty();
        }

        size_type size() const
        {
            return m_umap.size();
        }

        // 实现NdbObjectCloneable接口
    public:
        virtual NdbPropertySchemaType GetSchemaType() const override
        {
            return NdbPropertySchemaType::UnorderedMap;
        }

        virtual OwnerPtr<NdbObjectContainer> NdbClone() const override
        {
            OwnerPtr<NdbUnorderedMap> opCloned = NEW_AS_OWNER_PTR(NdbUnorderedMap);
            for (auto ite = m_umap.begin(); ite != m_umap.end(); ++ite)
            {
                Key k = NdbCopyHelper<Key>::Copy(ite->first);
                T t = NdbCopyHelper<T>::Copy(ite->second);
                (*opCloned)[k] = std::move(t);
            }
            return TransferOwnership(opCloned);
        }

        virtual void NdbAssignByMove(OwnerPtr<NdbObjectContainer> opSrc) override
        {
            NdbUnorderedMap * pMap = dynamic_cast<NdbUnorderedMap*>(opSrc.get());
            if (pMap)
            {
                *this = std::move(*pMap);
            }
        }

        virtual UInt64 NdbSerialize(IDbObjectArchive & objectArchive, NdbPropertyUtils::BehavioursAdapter adapter) const override
        {
            UInt64 nSize = size();
            NdbContainerArchiveBlockHeader containerArchiveBlockHeader(NdbContainerArchiveBlockHeader::s_CurrentVersion, nSize, NdbPropertySchemaType::UnorderedMap);

            IIOStream& ioStream = objectArchive.GetIOStream();
            IIODevice* pIODevice = ioStream.GetDevice();
            DBG_WARN_AND_RETURN_UNLESS(pIODevice, 0, L"写入的目标设备为空", L"GMEP", L"2020/10/11");
            std::vector<std::vector<char>> dataVec;
            dataVec.reserve(m_umap.size());

            if (property_is_value_type<Key>::value && property_is_value_type<T>::value)
            {
                NdbArchiveBlockHeader blockHeader = GetSharedBlockHeader<Key>();
                containerArchiveBlockHeader.SetSharedHeader(blockHeader, 0);

                blockHeader = GetSharedBlockHeader<T>();
                containerArchiveBlockHeader.SetSharedHeader(blockHeader, 1);

                UInt64 length = containerArchiveBlockHeader.Serialize(objectArchive.GetIOStream());
                const UInt64 bodyBegin = pIODevice->Pos();

                for (auto ite = m_umap.begin(); ite != m_umap.end(); ++ite)
                {
                    UInt64 tempLength = 0;
                    tempLength += NdbPropertySerializeWithoutHeader<Key>::Serialize(ite->first, objectArchive, adapter);
                    tempLength += NdbPropertySerializeWithoutHeader<T>::Serialize(ite->second, objectArchive, adapter);
                    length += tempLength;
                    OrderSerializeUnorderedMapUtils::CollectSerializeData(ioStream, dataVec, tempLength);
                }
                OrderSerializeUnorderedMapUtils::BackfillOrderedSerializeData(ioStream, dataVec, bodyBegin);
                return length;
            }
            else
            {
                UInt64 length = containerArchiveBlockHeader.Serialize(objectArchive.GetIOStream());
                const UInt64 bodyBegin = pIODevice->Pos();

                for (auto ite = m_umap.begin(); ite != m_umap.end(); ++ite)
                {
                    UInt64 tempLength = 0;
                    tempLength += NdbPropertySerialize<Key>::Serialize(ite->first, objectArchive, adapter);
                    tempLength += NdbPropertySerialize<T>::Serialize(ite->second, objectArchive, adapter);
                    length += tempLength;
                    OrderSerializeUnorderedMapUtils::CollectSerializeData(ioStream, dataVec, tempLength);
                }
                OrderSerializeUnorderedMapUtils::BackfillOrderedSerializeData(ioStream, dataVec, bodyBegin);
                return length;
            }
        }

        virtual void NdbExtractObjectFromMemory(NdbObjectMemoryModelNode * pObjMemModel, UInt32 majorVersion) override
        {
            NdbObjectMemoryModelGroupNode *pMemGroupNode = thread_safe_quick_cast<NdbObjectMemoryModelGroupNode>(pObjMemModel);
            DBG_WARN_AND_RETURN_VOID_UNLESS(pMemGroupNode, L"pMemGroupNode为空？", L"GMEP", L"2020/11/24");
            std::vector<OwnerPtr<NdbObjectMemoryModelNode>>& subNodes = pMemGroupNode->GetSubNodesFW();
            UInt32 nSize = (UInt32)subNodes.size();
            for (UInt32 i = 0; i < nSize; i = i + 2)
            {
                NdbObjectMemoryModelNode *pSubNode = subNodes[i].get();
                if (!pSubNode) continue;
                Key k;
                switch (pSubNode->GetSchemaType())
                {
                case NdbPropertySchemaType::Value:
                    NdbObjectArchiveHelper::GetValueFromMemoryModel(&k, pSubNode);
                    break;
                case NdbPropertySchemaType::ElementId:
                    NdbObjectArchiveHelper::GetElementIdFromMemoryModel(&k, pSubNode);
                    break;
                case NdbPropertySchemaType::WString:
                    NdbObjectArchiveHelper::GetWStringFromMemoryModel(&k, pSubNode);
                    break;
                case NdbPropertySchemaType::SchematicObject:
                    NdbObjectArchiveHelper::GetSchematicObjectFromMemoryModel(&k, pSubNode, false/*isParentSubObject*/, majorVersion);
                    break;
                case NdbPropertySchemaType::CloneableObject:
                    NdbObjectArchiveHelper::GetCloneableObjectFromMemoryModel(&k, pSubNode, majorVersion);
                    break;
                case NdbPropertySchemaType::OwnerPtrToNdbObject:
                    NdbObjectArchiveHelper::GetOwnerPtrFromMemoryModel(&k, pSubNode, majorVersion);
                    break;
                case NdbPropertySchemaType::NativePointer:
                    NdbObjectArchiveHelper::GetPointerFromMemoryModel(&k, pSubNode);
                default:
                    DBG_WARN(L"NdbUnorderedMap Key: 不支持存放的类型" + NdbPropertyUtils::SchemaTypeAsWString(pSubNode->GetSchemaType()), L"GMEP", L"2017-01-16");
                    break;
                }

                pSubNode = subNodes[i + 1].get();
                if (!pSubNode) continue;
                T t;
                switch (pSubNode->GetSchemaType())
                {
                case NdbPropertySchemaType::Value:
                    NdbObjectArchiveHelper::GetValueFromMemoryModel(&t, pSubNode);
                    break;
                case NdbPropertySchemaType::ElementId:
                    NdbObjectArchiveHelper::GetElementIdFromMemoryModel(&t, pSubNode);
                    break;
                case NdbPropertySchemaType::WString:
                    NdbObjectArchiveHelper::GetWStringFromMemoryModel(&t, pSubNode);
                    break;
                case NdbPropertySchemaType::SchematicObject:
                    NdbObjectArchiveHelper::GetSchematicObjectFromMemoryModel(&t, pSubNode, false/*isParentSubObject*/, majorVersion);
                    break;
                case NdbPropertySchemaType::CloneableObject:
                    NdbObjectArchiveHelper::GetCloneableObjectFromMemoryModel(&t, pSubNode, majorVersion);
                    break;
                case NdbPropertySchemaType::OwnerPtrToNdbObject:
                    NdbObjectArchiveHelper::GetOwnerPtrFromMemoryModel(&t, pSubNode, majorVersion);
                    break;
                case NdbPropertySchemaType::OwnerPtrToIDbObject:
                    NdbObjectArchiveHelper::GetOwnerPtrToIDbObjectFromMemoryModel(&t, pSubNode, majorVersion);
                    break;
                case NdbPropertySchemaType::NativePointer:
                    NdbObjectArchiveHelper::GetPointerFromMemoryModel(&t, pSubNode);
                    break;
                default:
                    DBG_WARN(L"NdbUnorderedMap Value: 不支持存放的类型" + NdbPropertyUtils::SchemaTypeAsWString(pSubNode->GetSchemaType()), L"GMEP", L"2019-03-30");
                    break;
                }
                m_umap.insert(std::make_pair(k, std::move(t)));
            }
        }

        virtual bool HasElementIdProperty() const override 
        {
            if (m_umap.empty()) return false; 

            // Key或Value的类型是OwnerPtr时，不能通过第一个元素确定。详见ModelIdDetectorHelper的OwnerPtr特化版本说明
            auto ite = m_umap.begin();
            return ElementIdDetectorHelper<Key>::DetectElementId(&(ite->first)) ||
                ElementIdDetectorHelper<T>::DetectElementId(&(ite->second));
        }

        virtual void ReplaceElementIds(const std::unordered_map<ElementId, ElementId, ElementIdHash>& oldToNewMap) override
        {
            if (!HasElementIdProperty()) return;

            std::unordered_map<Key, T, Hash, KeyEqual> umap;
            for (auto ite = m_umap.begin(); ite != m_umap.end(); ++ite)
            {
                Key k = NdbCopyHelper<Key>::Copy(ite->first);
                ReplaceElementIdHelper<Key>::ReplaceElementId(&k, oldToNewMap);

                T t = NdbCopyHelper<T>::Copy(ite->second);
                ReplaceElementIdHelper<T>::ReplaceElementId(&t, oldToNewMap);

                umap[k] = std::move(t);
            }
            m_umap.swap(umap);
        }

        virtual void ReportElementIds(
            std::set<ElementId>* pModelIds,
            std::unordered_map<ElementId, 
            std::vector<std::wstring>, ElementIdHash>* pPaths,
            std::vector<std::wstring>* pCurrentPath
        ) const override
        {
            DBG_WARN_AND_RETURN_VOID_UNLESS(pModelIds, L"pModelIds不应该为空", L"GMEP", L"2016/10/14");
            if (!HasElementIdProperty()) return;

            for (auto ite = m_umap.begin(); ite != m_umap.end(); ++ite)
            {
                ReportElementIdHelper<Key>::ReportElementId(ite->first, pModelIds, pPaths, pCurrentPath);
                ReportElementIdHelper<T>::ReportElementId(ite->second, pModelIds, pPaths, pCurrentPath);
            }
        }

        virtual void CollectModuleGroupsOfNdbObject(std::set<Guid, GuidComparer>& moduleGroupsGuids) const
        {
            for (auto ite = m_umap.begin(); ite != m_umap.end(); ++ite)
            {
                NdbPropertySerialize<T>::CollectModuleGroups(ite->second, moduleGroupsGuids);
            }
        }

        virtual void CheckTopOwnerObject(std::vector<const UniqueWString*>& path,const NdbObject* correctowner,
            const NdbClassSchema* pNdbClassSchema, std::vector<std::wstring>& resultstr) const override
        {
            const std::wstring& subpropertyname = UniqueWString::Find(L"")->GetContent();
            std::vector<const UniqueWString*> localpath;
            localpath.insert(localpath.end(), path.begin(), path.end());
            localpath.push_back(UniqueWString::Find(L""));

            for (auto ite = m_umap.begin(); ite != m_umap.end(); ++ite)
            {
                if (!std::is_base_of<IDbObject, T>::value)
                {
                    auto result = Convert_Type_If<std::is_base_of<NdbObject, T>::value, property_is_owner_ptr_type<T>::value, NdbObject, T>::GetData(ite->second);
                    NdbObject* pObject = GetNonConstPoint<NdbObject*>::GetNonConstPointImp(result);

                    if (pObject != nullptr)
                    {
                        pObject->CheckTopOwnerObject(localpath, correctowner, nullptr, resultstr);

                    }
                }
                else
                {
                    auto result = Convert_Type_If<std::is_base_of<IDbObject, T>::value, property_is_owner_ptr_type<T>::value, IDbObject, T>::GetData(ite->second);
                    IDbObject* pPropertyIDbObject = GetNonConstPoint<IDbObject*>::GetNonConstPointImp(result);
                    if (pPropertyIDbObject != nullptr)
                    {
                        NdbObject* pPropertyNDbObject = dynamic_cast<NdbObject*>(pPropertyIDbObject);
                        if (pPropertyNDbObject == nullptr)
                            continue;

                        pPropertyNDbObject->CheckTopOwnerObject(localpath, correctowner, nullptr, resultstr);

                    }

                }

                if (!std::is_base_of<IDbObject, Key>::value)
                {
                    auto result = Convert_Type_If<std::is_base_of<NdbObject, Key>::value, property_is_owner_ptr_type<Key>::value, NdbObject, Key>::GetData(ite->first);
                    NdbObject* pObject = GetNonConstPoint<NdbObject*>::GetNonConstPointImp(result);

                    if (pObject != nullptr)
                    {
                        pObject->CheckTopOwnerObject(localpath, correctowner, nullptr, resultstr);

                    }

                }
                else
                {
                    auto result = Convert_Type_If<std::is_base_of<IDbObject, Key>::value, property_is_owner_ptr_type<Key>::value, IDbObject, Key>::GetData(ite->first);
                    IDbObject* pPropertyIDbObject = GetNonConstPoint<IDbObject*>::GetNonConstPointImp(result);
                    if (pPropertyIDbObject != nullptr)
                    {
                        NdbObject* pPropertyNDbObject = dynamic_cast<NdbObject*>(pPropertyIDbObject);
                        if (pPropertyNDbObject == nullptr)
                            continue;

                        pPropertyNDbObject->CheckTopOwnerObject(localpath, correctowner, nullptr, resultstr);

                    }

                }
            }

        }

        private:
            std::unordered_map<Key, T, Hash, KeyEqual> m_umap;
    };
}
